import 'dart:math' as math;
import 'dart:ui' as ui;

import 'package:boss/color_constant.dart';
import 'package:boss/textStyle/text_style.dart';
import 'package:flutter/material.dart';

typedef PieLabelBuilder<T> = String Function(PieData<T>);

class PieChart<T extends num> extends StatefulWidget {
  final List<PieData<T>> data;
  final int showLabelIndex;
  final PieLabelBuilder labelBuilder;
  final ValueChanged<int> onLabelSelectedChanged;

  const PieChart({
    Key key,
    @required this.data,
    @required this.labelBuilder,
    this.showLabelIndex,
    this.onLabelSelectedChanged,
  }) : super(key: key);

  @override
  _PieChartState<T> createState() => _PieChartState<T>();
}

class _PieChartState<T extends num> extends State<PieChart<T>> {
  List<PieData<T>> get pieData => widget.data.where((item) => item.value > 0).toList();

  @override
  Widget build(BuildContext context) {
    return Stack(
      alignment: Alignment.center,
      children: <Widget>[
        Column(
          children: <Widget>[
            Container(
              width: 114,
              height: 114,
              child: CustomPaint(
                painter: PieChartPainter<T>(
                  pieData,
                  labelBuilder: widget.labelBuilder,
                  showLabelIndex: widget.showLabelIndex,
                ),
              ),
            ),
            Container(
              margin: EdgeInsets.only(top: 28),
              height: 60,
              child: ListView.builder(
                padding: EdgeInsets.all(0),
                scrollDirection: Axis.horizontal,
                itemCount: pieData.length,
                itemBuilder: ((context, index) {
                  final current = pieData[index];

                  return GestureDetector(
                    onTap: () {
                      if (widget.onLabelSelectedChanged != null) {
                        widget.onLabelSelectedChanged(index);
                      }
                    },
                    behavior: HitTestBehavior.translucent,
                    child: Container(
                      padding: EdgeInsets.symmetric(
                        horizontal: 15,
                      ),
                      child: Row(
                        children: <Widget>[
                          Container(
                            margin: EdgeInsets.only(right: 5),
                            child: Stack(
                              alignment: Alignment.center,
                              children: <Widget>[
                                Container(
                                  width: 9,
                                  height: 9,
                                  decoration: BoxDecoration(
                                    shape: BoxShape.circle,
                                    color: current.color,
                                  ),
                                ),
                                Transform.rotate(
                                  angle: math.pi / 180 * 45,
                                  child: Container(
                                    width: 3,
                                    height: 3,
                                    color: Colors.white,
                                  ),
                                ),
                              ],
                            ),
                          ),
                          Text(
                            current.label,
                            style: TextStyle(
                              color: widget.showLabelIndex == index ? ColorConstant.ff33 : ColorConstant.ffcc,
                              fontSize: 14,
                            ),
                          ),
                        ],
                      ),
                    ),
                  );
                }),
              ),
            ),
          ],
        ),
        if (pieData.length == 0)
          Text(
            '暂无数据',
            style: TextStyleConstant().normal_1_28,
          )
      ],
    );
  }
}

class PieChartPainter<T extends num> extends CustomPainter {
  List<PieData<T>> data;
  PieLabelBuilder<T> labelBuilder;
  int showLabelIndex;

  double strokeWidth = 9;

  List<double> labelRightYs = [];
  List<double> labelLeftYs = [];
  List<double> angles = [];

  double get prevAngle => angles.last;
  final double minDistanceY = 28;

  //总数
  T get total {
    num number = 0;

    data.forEach((item) {
      number += item.value;
    });

    return number;
  }

  PieChartPainter(
    this.data, {
    Key key,
    this.labelBuilder,
    this.showLabelIndex,
  });

  @override
  void paint(Canvas canvas, Size size) {
    angles = [math.pi / 180 * -90];
    labelLeftYs = [];
    labelRightYs = [];

    data.forEach((item) {
      drawArc(canvas, size, item);
    });

    if (showLabelIndex == null) {
      data.forEach((item) {
        drawLabel(canvas, size, item);
      });
    } else if (showLabelIndex < data.length){
      drawLabel(canvas, size, data[showLabelIndex]);
    }
  }

  //画弧线
  drawArc(Canvas canvas, Size size, PieData data) {
    var rectSize = math.min(size.width, size.height);

    var translateX = 0.0;
    var translateY = 0.0;
    var rectWidth = rectSize;
    var rectHeight = rectSize;

    //区域
    var rect = Rect.fromLTWH(translateX, translateY, rectWidth, rectHeight);

    //百分比
    double percentage = data.value / total;

    //弧度
    double angle = (math.pi / 180) * 360 * percentage;

    //画笔
    final Paint paint = Paint()
      ..isAntiAlias = true
      ..color = data.color
      ..strokeWidth = strokeWidth
      ..style = PaintingStyle.stroke;

    canvas.drawArc(rect, prevAngle, angle, false, paint);

    angles.add(prevAngle + angle);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }

  double convertRadiusToSigma(double radius) {
    return radius * 0.57735 + 0.5;
  }

  drawLabel(Canvas canvas, Size size, PieData<T> data) {
    var rectSize = math.min(size.width, size.height);
    var radius = rectSize / 2;

    //百分比
    double percentage = data.value / total;

    var prevAngle = angles[this.data.indexOf(data)];

    //弧度
    double angle = (math.pi / 180) * 360 * percentage / 2 + prevAngle;

    var centerPoint = Offset(radius, radius);

    var point = Offset(
      centerPoint.dx + math.cos(angle) * rectSize / 2,
      centerPoint.dy + math.sin(angle) * rectSize / 2,
    );

    Paint paint = Paint()
      ..color = ColorConstant.ccff
      ..style = PaintingStyle.stroke
      ..strokeWidth = 1;

    Offset labelStartPoint = Offset(point.dx, point.dy);
    Offset labelCenterPoint;
    Offset labelEndPoint;
    PointQuadrant pointQuadrant;

    if (point.dx >= radius && point.dy < radius) {
      //右上
      var dy = mathY(point.dy, PointQuadrant.topRight);
      labelCenterPoint = Offset(point.dx + 10, dy);
      labelEndPoint = Offset(point.dx + 20, dy);

      pointQuadrant = PointQuadrant.topRight;
    } else if (point.dx >= radius && point.dy >= radius) {
      //右下
      var dy = mathY(point.dy, PointQuadrant.bottomRight);
      labelCenterPoint = Offset(point.dx + 10, dy);
      labelEndPoint = Offset(point.dx + 20, dy);

      pointQuadrant = PointQuadrant.bottomRight;
    } else if (point.dx < radius && point.dy < radius) {
      //左上
      var dy = mathY(point.dy, PointQuadrant.topLeft);
      labelCenterPoint = Offset(point.dx - 10, dy);
      labelEndPoint = Offset(point.dx - 20, dy);

      pointQuadrant = PointQuadrant.topLeft;
    } else {
      //左下
      var dy = mathY(point.dy, PointQuadrant.bottomLeft);
      labelCenterPoint = Offset(point.dx - 10, dy);
      labelEndPoint = Offset(point.dx - 20, dy);

      pointQuadrant = PointQuadrant.bottomLeft;
    }

    Path path = Path();
    path.moveTo(labelStartPoint.dx, labelStartPoint.dy);
    path.lineTo(labelCenterPoint.dx, labelCenterPoint.dy);
    path.lineTo(labelEndPoint.dx, labelEndPoint.dy);

    canvas.drawPath(path, paint);

    var isNeedDealLabel = [PointQuadrant.topLeft, PointQuadrant.bottomLeft].contains(pointQuadrant);

    ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle(
      textAlign: isNeedDealLabel ? TextAlign.end : TextAlign.start,
      fontWeight: FontWeight.w300,
      fontStyle: FontStyle.normal,
      fontSize: 12,
    ));
    pb.pushStyle(ui.TextStyle(color: Colors.black));

    var labelText = labelBuilder == null ? data.label : labelBuilder(data);

    pb.addText(labelText);

    ui.ParagraphConstraints pc = ui.ParagraphConstraints(width: 100);
    //这里需要先layout, 后面才能获取到文字高度
    ui.Paragraph paragraph = pb.build()..layout(pc);

    //label偏移距离
    Offset labelOffset = Offset(
      isNeedDealLabel ? -100 : 0,
      -paragraph.height / 2,
    );

    canvas.drawParagraph(paragraph, labelEndPoint + labelOffset);
  }

  double mathY(double dy, PointQuadrant quadrant) {
    List tempYs = [];

    if (quadrant == PointQuadrant.bottomLeft) {
      dy += 8;

      tempYs = labelLeftYs;
    } else if (quadrant == PointQuadrant.bottomRight) {
      dy += 8;

      tempYs = labelRightYs;
    } else if (quadrant == PointQuadrant.topLeft) {
      dy -= 8;

      tempYs = labelLeftYs;
    } else if (quadrant == PointQuadrant.topRight) {
      dy -= 8;

      tempYs = labelRightYs;
    }

    if (tempYs.length > 0) {
      dy = (dy - tempYs.last).abs() < minDistanceY ? tempYs.last + minDistanceY : dy;
    }

    tempYs.add(dy);

    return dy;
  }
}

class PieData<T> {
  String label;
  T value;
  Color color;

  PieData(this.label, this.value, this.color);
}

enum PointQuadrant {
  topLeft,
  topRight,
  bottomRight,
  bottomLeft,
}
